/*Copyright ©2015 TommyLemon(https://github.com/TommyLemon)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
limitations under the License.*/

package zuo.biao.library.manager;

import com.alibaba.fastjson.JSON;

import ohos.app.Context;
import ohos.app.dispatcher.TaskDispatcher;
import ohos.app.dispatcher.task.TaskPriority;
import ohos.data.DatabaseHelper;
import ohos.data.preferences.Preferences;
import okhttp3.*;
import zuo.biao.library.interfaces.OnHttpResponseListener;
import zuo.biao.library.model.Parameter;
import zuo.biao.library.util.Log;
import zuo.biao.library.util.StringUtil;

import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.TimeUnit;

/**HTTP请求管理类
 * @author Lemon
 * @use HttpManager.getInstance().get(...)或HttpManager.getInstance().post(...)  > 在回调方法onHttpRequestSuccess和onHttpRequestError处理HTTP请求结果
 * @must 解决getToken，getResponseCode，getResponseData中的
 */
public class HttpManager {
    private static final String KEY_COOKIE = "cookie";

    private static final String TAG = "HttpManager";

    private static Context context;


    /**
     * 初始化
     * @param cont 上下文
     */
    public static void init(Context cont) {
        context = cont;
        OkHttpClient okHttpClient =
                new OkHttpClient.Builder()
                        .connectionSpecs(
                                Arrays.asList(
                                        ConnectionSpec.MODERN_TLS,
                                        ConnectionSpec.COMPATIBLE_TLS,
                                        ConnectionSpec.CLEARTEXT))
                        .build();
    }

    private static HttpManager instance; // 单例

    /**
     * 单例模式
     * @return r
     */
    public static HttpManager getInstance() {
        if (instance == null) {
            synchronized (HttpManager.class) {
                if (instance == null) {
                    instance = new HttpManager();
                }
            }
        }
        return instance;
    }

    /**GET请求
     * @param request 请求
     * @param url 网络地址
     * @param requestCode requestCode
     *            请求码，类似onActivityResult中请求码，当同一activity中以实现接口方式发起多个网络请求时，请求结束后都会回调
     *            {@link OnHttpResponseListener#onHttpResponse(int, String, Exception)}<br>
     *            在发起请求的类中可以用requestCode来区分各个请求
     * @param listener listener
     */
    public void get(
            final Map<String, Object> request,
            final String url,
            final int requestCode,
            final OnHttpResponseListener listener) {
        TaskDispatcher dispatcher = context.createParallelTaskDispatcher(url, TaskPriority.DEFAULT);

        dispatcher.asyncDispatch(
                new Runnable() {
                    @Override
                    public void run() {
                        Exception ex = null;
                        String respond = null;

                        OkHttpClient client = getHttpClient(url);
                        if (client == null) {
                            new Exception(TAG + ".get  TaskDispatcher  client == null >> return;");
                        }

                        StringBuffer sb = new StringBuffer();
                        sb.append(url);

                        Set<Map.Entry<String, Object>> set = request == null ? null : request.entrySet();
                        if (set != null) {
                            boolean isFirst = true;
                            for (Map.Entry<String, Object> entry : set) {
                                sb.append(isFirst ? "?" : "&");
                                sb.append(StringUtil.trim(entry.getKey()));
                                sb.append("=");
                                sb.append(StringUtil.trim(entry.getValue()));

                                isFirst = false;
                            }
                        }

                        try {
                            respond = getResponseJson(client, new Request.Builder().url(sb.toString()).build());
                        } catch (Exception e) {
                            Log.error(
                                    TAG,
                                    "get  AsyncTask.doInBackground  try {  result = getResponseJson(..."
                                            + "} catch (Exception e) {"
                                            + e.getMessage());
                            ex = e;
                        }
                        listener.onHttpResponse(requestCode, respond, ex);
                    }
                });
    }

    /**GET请求
     * @param paramList 请求参数
     * @param url 网络地址
     * @param requestCode requestCode
     *            请求码，类似onActivityResult中请求码，当同一activity中以实现接口方式发起多个网络请求时，请求结束后都会回调
     *            {@link OnHttpResponseListener#onHttpResponse(int, String, Exception)}<br>
     *            在发起请求的类中可以用requestCode来区分各个请求
     * @param listener listener
     */
    public void get(
            final List<Parameter> paramList,
            final String url,
            final int requestCode,
            final OnHttpResponseListener listener) {
        Map<String, Object> request = new HashMap<>();
        if (paramList != null) {
            for (Parameter p : paramList) {
                request.put(p.key, p.value);
            }
        }
        get(request, url, requestCode, listener);
    }

    /**
     * MediaType 类型
     */
    public static final MediaType TYPE_JSON = MediaType.parse("application/json; charset=utf-8");

    /**POST请求，以FORM表单形式提交
     * @param request 请求
     * @param url 网络地址
     * @param requestCode requestCode
     *            请求码，类似onActivityResult中请求码，当同一activity中以实现接口方式发起多个网络请求时，请求结束后都会回调
     *            {@link OnHttpResponseListener#onHttpResponse(int, String, Exception)}<br/>
     *            在发起请求的类中可以用requestCode来区分各个请求
     * @param listener listener
     */
    public void post(
            final Map<String, Object> request,
            final String url,
            final int requestCode,
            final OnHttpResponseListener listener) {
        post(request, url, false, requestCode, listener);
    }
    /**POST请求，以FORM表单形式提交
     * @param paramList 请求参数
     * @param url 网络地址
     * @param requestCode requestCode
     *            请求码，类似onActivityResult中请求码，当同一activity中以实现接口方式发起多个网络请求时，请求结束后都会回调
     *            {@link OnHttpResponseListener#onHttpResponse(int, String, Exception)}<br/>
     *            在发起请求的类中可以用requestCode来区分各个请求
     * @param listener listener
     */
    public void post(
            final List<Parameter> paramList,
            final String url,
            final int requestCode,
            final OnHttpResponseListener listener) {
        Map<String, Object> request = new HashMap<>();
        if (paramList != null) {
            for (Parameter p : paramList) {
                request.put(p.key, p.value);
            }
        }
        post(request, url, false, requestCode, listener);
    }
    /**POST请求
     * @param request 请求
     * @param url 网络地址
     * @param isJson JSON : FORM
     * @param requestCode
     *            请求码，类似onActivityResult中请求码，当同一activity中以实现接口方式发起多个网络请求时，请求结束后都会回调
     *            {@link OnHttpResponseListener#onHttpResponse(int, String, Exception)}<br/>
     *            在发起请求的类中可以用requestCode来区分各个请求
     * @param listener listener
     */
    public void post(
            final Map<String, Object> request,
            final String url,
            final boolean isJson,
            final int requestCode,
            final OnHttpResponseListener listener) {
        TaskDispatcher dispatcher = context.createParallelTaskDispatcher(url, TaskPriority.DEFAULT);

        dispatcher.asyncDispatch(
                new Runnable() {
                    @Override
                    public void run() {
                        Exception ex = null;
                        String respond = null;
                        try {
                            OkHttpClient client = getHttpClient(url);
                            if (client == null) {
                                new Exception(TAG + ".post  AsyncTask.doInBackground  client == null >> return;");
                            }

                            RequestBody requestBody;
                            if (isJson) {
                                String body = JSON.toJSONString(request);
                                Log.debug(
                                        TAG,
                                        "\n\n<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<\n post  url = "
                                                + url
                                                + " request = "
                                                + body);
                                requestBody = RequestBody.create(TYPE_JSON, body);
                            } else {
                                FormBody.Builder builder = new FormBody.Builder();
                                Set<Map.Entry<String, Object>> set = request == null ? null : request.entrySet();
                                if (set != null) {
                                    for (Map.Entry<String, Object> entry : set) {
                                        builder.add(StringUtil.trim(entry.getKey()), StringUtil.trim(entry.getValue()));
                                    }
                                }

                                requestBody = builder.build();
                            }

                            respond = getResponseJson(client, new Request.Builder().url(url).post(requestBody).build());

                        } catch (Exception e) {
                            Log.error(
                                    TAG,
                                    "post  AsyncTask.doInBackground  try {  result = getResponseJson(..."
                                            + "} catch (Exception e) {"
                                            + e.getMessage());
                            ex = e;
                        }
                        listener.onHttpResponse(requestCode, respond, ex);
                    }
                });
    }

    // httpGet/httpPost 内调用方法 <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<

    /**
     * 初始化
     * @param url url
     * @return OkHttpClient
     */
    private OkHttpClient getHttpClient(String url) {
        Log.info(TAG, "getHttpClient  url = " + url);
        if (StringUtil.isEmpty(url)) {
            Log.error(TAG, "getHttpClient  StringUtil.isEmpty(url) >> return null;");
            return null;
        }

        OkHttpClient.Builder builder =
                new OkHttpClient.Builder()
                        .connectTimeout(15, TimeUnit.SECONDS)
                        .writeTimeout(10, TimeUnit.SECONDS)
                        .readTimeout(10, TimeUnit.SECONDS);

        return builder.build();
    }

    /**
     * getCookie
     * @param host host
     * @return String
     */
    public String getCookie(String host) {
        if (host == null) {
            Log.error(TAG, "getCookie  host == null >> return \"\"");
            return "";
        }
        DatabaseHelper helper = new DatabaseHelper(context);
        Preferences pr = helper.getPreferences(KEY_COOKIE);

        return pr.getString(host, "");
    }
    /**saveCookie
     * @param host host
     * @param value value
     */
    public void saveCookie(String host, String value) {
        if (host == null) {
            Log.error(TAG, "saveCookie  host == null >> return;");
            return;
        }
        DatabaseHelper helper = new DatabaseHelper(context);
        Preferences pr = helper.getPreferences(KEY_COOKIE);
        pr.delete(host);
        pr.putString(host, value);
        pr.flushSync();
    }

    /**
     * getResponseJson
     * @param client client
     * @param request request
     * @return String
     * @throws Exception Exception
     */
    private String getResponseJson(OkHttpClient client, Request request) throws Exception {
        if (client == null || request == null) {
            Log.error(TAG, "getResponseJson  client == null || request == null >> return null;");
            return null;
        }
        Response response = client.newCall(request).execute();
        return response.isSuccessful() ? response.body().string() : null;
    }

    // httpGet/httpPost 内调用方法 >>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>

}
